msg_tool\scripts\kirikiri/
ks.rs1use crate::ext::fancy_regex::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::escape::*;
7use anyhow::Result;
8use fancy_regex::Regex;
9use std::collections::HashSet;
10use std::io::Write;
11use std::ops::{Deref, DerefMut, Index, IndexMut};
12use std::sync::Arc;
13
14#[derive(Debug)]
15pub struct KsBuilder {}
17
18impl KsBuilder {
19 pub fn new() -> Self {
21 Self {}
22 }
23}
24
25impl ScriptBuilder for KsBuilder {
26 fn default_encoding(&self) -> Encoding {
27 Encoding::Cp932
28 }
29
30 fn build_script(
31 &self,
32 buf: Vec<u8>,
33 _filename: &str,
34 encoding: Encoding,
35 _archive_encoding: Encoding,
36 config: &ExtraConfig,
37 _archive: Option<&Box<dyn Script>>,
38 ) -> Result<Box<dyn Script>> {
39 Ok(Box::new(KsScript::new(buf, encoding, config)?))
40 }
41
42 fn extensions(&self) -> &'static [&'static str] {
43 &["ks", "soc"]
44 }
45
46 fn script_type(&self) -> &'static ScriptType {
47 &ScriptType::Kirikiri
48 }
49}
50
51pub trait Node {
53 fn serialize(&self) -> String;
55}
56
57#[derive(Clone, Debug)]
58pub struct CommentNode(pub String);
60
61impl Node for CommentNode {
62 fn serialize(&self) -> String {
63 format!("; {}", self.0)
64 }
65}
66
67#[derive(Clone, Debug)]
68pub struct LabelNode {
70 pub name: String,
72 pub page: Option<String>,
74}
75
76impl Node for LabelNode {
77 fn serialize(&self) -> String {
78 if let Some(page) = &self.page {
79 format!("*{}|{}", self.name, page)
80 } else {
81 format!("*{}", self.name)
82 }
83 }
84}
85
86#[derive(Clone, Debug)]
87pub struct TextNode(pub String);
89
90impl Node for TextNode {
91 fn serialize(&self) -> String {
92 self.0.replace("[", "[[")
94 }
95}
96
97#[derive(Clone, Debug)]
98pub struct EmptyLineNode;
100
101impl Node for EmptyLineNode {
102 fn serialize(&self) -> String {
103 String::new()
104 }
105}
106
107#[derive(Clone, Debug)]
108pub enum TagAttr {
110 True,
112 Str(String),
114}
115
116#[derive(Clone, Debug)]
117pub struct TagNode {
119 pub name: String,
121 pub attributes: Vec<(String, TagAttr)>,
123}
124
125impl TagNode {
126 fn serialize_attributes(&self) -> String {
127 let mut parts = Vec::new();
128 for (key, value) in self.attributes.iter() {
129 match value {
130 TagAttr::True => {
131 parts.push(key.clone());
132 }
133 TagAttr::Str(val) => {
134 if val.contains(" ") || val.contains("=") {
135 parts.push(format!("{}=\"{}\"", key, val));
136 } else {
137 parts.push(format!("{}={}", key, val));
138 }
139 }
140 }
141 }
142 parts.join(" ")
143 }
144
145 fn ser_attributes_xml(&self) -> String {
146 let mut parts = Vec::new();
147 for (key, value) in self.attributes.iter() {
148 match value {
149 TagAttr::True => {
150 parts.push(key.clone());
151 }
152 TagAttr::Str(val) => {
153 parts.push(format!("{}=\"{}\"", key, escape_xml_attr_value(val)));
154 }
155 }
156 }
157 parts.join(" ")
158 }
159
160 pub fn set_attr(&mut self, key: &str, value: String) {
162 if let Some(attr) = self.attributes.iter_mut().find(|(k, _)| k == key) {
163 attr.1 = TagAttr::Str(value);
164 } else {
165 self.attributes.push((key.to_string(), TagAttr::Str(value)));
166 }
167 }
168
169 fn to_xml_tag(&self) -> String {
170 let attr_str = self.ser_attributes_xml();
171 if attr_str.is_empty() {
172 format!("<{}>", self.name)
173 } else {
174 format!("<{} {}>", self.name, attr_str)
175 }
176 }
177}
178
179impl Node for TagNode {
180 fn serialize(&self) -> String {
181 let attr_str = self.serialize_attributes();
182 if attr_str.is_empty() {
183 format!("[{}]", self.name)
184 } else {
185 format!("[{} {}]", self.name, attr_str)
186 }
187 }
188}
189
190#[derive(Clone)]
191pub struct CommandNode {
193 pub inner: TagNode,
195}
196
197impl Deref for CommandNode {
198 type Target = TagNode;
199
200 fn deref(&self) -> &Self::Target {
201 &self.inner
202 }
203}
204
205impl DerefMut for CommandNode {
206 fn deref_mut(&mut self) -> &mut Self::Target {
207 &mut self.inner
208 }
209}
210
211impl std::fmt::Debug for CommandNode {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 f.debug_struct("CommandNode")
214 .field("name", &self.inner.name)
215 .field("attributes", &self.inner.attributes)
216 .finish()
217 }
218}
219
220impl Node for CommandNode {
221 fn serialize(&self) -> String {
222 let attr_str = self.inner.serialize_attributes();
223 if attr_str.is_empty() {
224 format!("@{}", self.inner.name)
225 } else {
226 format!("@{} {}", self.inner.name, attr_str)
227 }
228 }
229}
230
231#[derive(Clone, Debug)]
232pub struct ScriptBlockNode(pub String);
234
235impl Node for ScriptBlockNode {
236 fn serialize(&self) -> String {
237 format!("[iscript]\n{}\n[endscript]", self.0)
238 }
239}
240
241#[derive(Clone, Debug)]
242pub enum ParsedLineNode {
244 Text(TextNode),
246 Tag(TagNode),
248}
249
250impl ParsedLineNode {
251 fn to_xml(&self) -> String {
252 match self {
253 ParsedLineNode::Text(text_node) => escape_xml_text_value(&text_node.0),
254 ParsedLineNode::Tag(tag_node) => {
255 if tag_node.name == "r" && tag_node.attributes.is_empty() {
256 "\n".to_string()
257 } else {
258 tag_node.to_xml_tag()
259 }
260 }
261 }
262 }
263
264 fn is_np(&self) -> bool {
265 matches!(self, ParsedLineNode::Tag(tag) if tag.name == "np")
266 }
267}
268
269impl Node for ParsedLineNode {
270 fn serialize(&self) -> String {
271 match self {
272 ParsedLineNode::Text(text_node) => text_node.serialize(),
273 ParsedLineNode::Tag(tag_node) => tag_node.serialize(),
274 }
275 }
276}
277
278#[derive(Clone, Debug)]
279pub struct ParsedLine(pub Vec<ParsedLineNode>);
281
282impl ParsedLine {
283 fn to_xml(&self) -> String {
284 let mut s = String::new();
285 for node in &self.0 {
286 s.push_str(&node.to_xml());
287 }
288 s
289 }
290}
291
292impl Deref for ParsedLine {
293 type Target = Vec<ParsedLineNode>;
294
295 fn deref(&self) -> &Self::Target {
296 &self.0
297 }
298}
299
300impl DerefMut for ParsedLine {
301 fn deref_mut(&mut self) -> &mut Self::Target {
302 &mut self.0
303 }
304}
305
306impl Node for ParsedLine {
307 fn serialize(&self) -> String {
308 self.0
309 .iter()
310 .map(|node| node.serialize())
311 .collect::<Vec<_>>()
312 .join("")
313 }
314}
315
316#[derive(Clone, Debug)]
317pub enum ParsedScriptNode {
319 Comment(CommentNode),
321 Label(LabelNode),
323 Command(CommandNode),
325 ScriptBlock(ScriptBlockNode),
327 Line(ParsedLine),
329 EmptyLine(EmptyLineNode),
331}
332
333impl ParsedScriptNode {
334 pub fn is_empty(&self) -> bool {
336 matches!(self, ParsedScriptNode::EmptyLine(_))
337 }
338
339 pub fn set_attr(&mut self, key: &str, value: String) {
341 if let ParsedScriptNode::Command(command) = self {
342 command.set_attr(key, value);
343 }
344 }
345}
346
347impl Node for ParsedScriptNode {
348 fn serialize(&self) -> String {
349 match self {
350 ParsedScriptNode::Comment(comment) => comment.serialize(),
351 ParsedScriptNode::Label(label) => label.serialize(),
352 ParsedScriptNode::Command(command) => command.serialize(),
353 ParsedScriptNode::ScriptBlock(script_block) => script_block.serialize(),
354 ParsedScriptNode::Line(line) => line.serialize(),
355 ParsedScriptNode::EmptyLine(empty_line) => empty_line.serialize(),
356 }
357 }
358}
359
360#[derive(Clone, Debug)]
361pub struct ParsedScript(pub Vec<ParsedScriptNode>);
363
364impl Deref for ParsedScript {
365 type Target = Vec<ParsedScriptNode>;
366
367 fn deref(&self) -> &Self::Target {
368 &self.0
369 }
370}
371
372impl DerefMut for ParsedScript {
373 fn deref_mut(&mut self) -> &mut Self::Target {
374 &mut self.0
375 }
376}
377
378impl Index<usize> for ParsedScript {
379 type Output = ParsedScriptNode;
380
381 fn index(&self, index: usize) -> &Self::Output {
382 &self.0[index]
383 }
384}
385
386impl IndexMut<usize> for ParsedScript {
387 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
388 if index < self.0.len() {
389 &mut self.0[index]
390 } else {
391 self.0.push(ParsedScriptNode::EmptyLine(EmptyLineNode));
392 self.0.last_mut().unwrap()
393 }
394 }
395}
396
397impl Node for ParsedScript {
398 fn serialize(&self) -> String {
399 self.0
400 .iter()
401 .map(|node| node.serialize())
402 .collect::<Vec<_>>()
403 .join("\n")
404 }
405}
406
407lazy_static::lazy_static! {
408 static ref LINE_SPLIT_RE: Regex = Regex::new(r"(\[.*?\])").unwrap();
409 static ref ATTR_RE: Regex = Regex::new("([a-zA-Z0-9_]+)(?:=(\"[^\"]*\"|'[^']*'|[^\\s\\]]+))?").unwrap();
410}
411
412pub struct Parser {
414 lines: Vec<String>,
415}
416
417impl Parser {
418 pub fn new(script: &str) -> Self {
422 let lines = script.lines().map(|s| s.to_string()).collect();
423 Self { lines }
424 }
425
426 fn parse_attributes(attr_str: &str) -> Result<Vec<(String, TagAttr)>> {
427 let mut attributes = Vec::new();
428 for cap in ATTR_RE.captures_iter(attr_str) {
429 let cap = cap?;
430 let key = cap
431 .get(1)
432 .ok_or(anyhow::anyhow!("Invalid attribute key"))?
433 .as_str()
434 .to_string();
435 let value = cap
436 .get(2)
437 .map(|v| {
438 let mut s = v.as_str().trim().to_string();
439 if s.starts_with("\"") && s.ends_with("\"") {
440 s = s[1..s.len() - 1].to_string();
441 } else if s.starts_with("'") && s.ends_with("'") {
442 s = s[1..s.len() - 1].to_string();
443 }
444 s = s.replace("`", "");
445 TagAttr::Str(s)
446 })
447 .unwrap_or(TagAttr::True);
448 attributes.push((key, value));
449 }
450 Ok(attributes)
451 }
452
453 fn parse_tag_or_command(content: &str) -> Result<TagNode> {
454 let parts = content.trim().split_ascii_whitespace().collect::<Vec<_>>();
455 let tag_name = parts[0].to_string();
456 let attr_string = parts[1..].join(" ");
457 let attrs = Self::parse_attributes(&attr_string)?;
458 Ok(TagNode {
459 name: tag_name,
460 attributes: attrs,
461 })
462 }
463
464 pub fn parse(&self, preserve_empty_lines: bool) -> Result<ParsedScript> {
468 let mut parsed_scripts = Vec::new();
469 let mut in_script_block = false;
470 let mut script_buffer = Vec::new();
471 let mut i = 0;
472 let line_count = self.lines.len();
473 while i < line_count {
474 let line = self.lines[i].trim();
475 i += 1;
476 if line.is_empty() {
477 if preserve_empty_lines {
478 parsed_scripts.push(ParsedScriptNode::EmptyLine(EmptyLineNode));
479 } else {
480 continue;
481 }
482 }
483 if in_script_block {
484 if line == "[endscript]" {
485 in_script_block = false;
486 parsed_scripts.push(ParsedScriptNode::ScriptBlock(ScriptBlockNode(
487 script_buffer.join("\n"),
488 )));
489 script_buffer.clear();
490 } else {
491 script_buffer.push(line.to_string());
492 }
493 continue;
494 }
495 if line == "[iscript]" {
496 in_script_block = true;
497 continue;
498 }
499 if line.starts_with(";") {
500 parsed_scripts.push(ParsedScriptNode::Comment(CommentNode(
501 line[1..].trim().to_string(),
502 )));
503 continue;
504 }
505 if line.starts_with("*") {
506 let parts: Vec<&str> = line.split('|').collect();
507 let label_name = parts[0][1..].trim().to_string();
508 let page = if parts.len() > 1 {
509 Some(parts[1..].join("|"))
510 } else {
511 None
512 };
513 parsed_scripts.push(ParsedScriptNode::Label(LabelNode {
514 name: label_name,
515 page,
516 }));
517 continue;
518 }
519 if line.starts_with("@") {
520 let content = &line[1..];
521 let tag_node = Self::parse_tag_or_command(content)?;
522 parsed_scripts.push(ParsedScriptNode::Command(CommandNode { inner: tag_node }));
523 continue;
524 }
525 let mut full_line = line.to_string();
526 while full_line.ends_with("\\") {
527 full_line.pop(); full_line = full_line.trim_end().to_string();
529 if i < line_count {
530 full_line.push(' ');
531 full_line.push_str(&self.lines[i].trim());
532 i += 1;
533 } else {
534 break; }
536 }
537 let mut parsed_line_nodes = Vec::new();
538 for part in LINE_SPLIT_RE.py_split(&full_line)? {
539 let part = part.trim();
540 if part.is_empty() {
541 continue;
542 }
543 if part.starts_with("[") && part.ends_with("]") {
544 if part == "[[r]]" {
545 parsed_line_nodes.push(ParsedLineNode::Text(TextNode("[r]".to_string())));
546 } else if part == "[[[[" {
547 parsed_line_nodes.push(ParsedLineNode::Text(TextNode("[[".to_string())));
548 } else if part.starts_with("[[") {
549 parsed_line_nodes
550 .push(ParsedLineNode::Text(TextNode(part[1..].to_string())))
551 } else {
552 parsed_line_nodes.push(ParsedLineNode::Tag(Self::parse_tag_or_command(
553 &part[1..part.len() - 1],
554 )?));
555 }
556 } else {
557 parsed_line_nodes.push(ParsedLineNode::Text(TextNode(part.to_string())));
558 }
559 }
560 if !parsed_line_nodes.is_empty() {
561 parsed_scripts.push(ParsedScriptNode::Line(ParsedLine(parsed_line_nodes)));
562 }
563 }
564 Ok(ParsedScript(parsed_scripts))
565 }
566}
567
568struct XMLTextParser {
569 str: String,
570 pos: usize,
571}
572
573impl XMLTextParser {
574 pub fn new(text: &str) -> Self {
575 Self {
576 str: text.replace("\n", "<r>"),
577 pos: 0,
578 }
579 }
580
581 fn parse_tag(&mut self) -> Result<TagNode> {
582 let mut name = String::new();
583 let mut attributes = Vec::new();
584 let mut is_name = true;
585 let mut is_key = false;
586 let mut is_value = false;
587 let mut is_in_quote = false;
588 let mut key = String::new();
589 let mut value = String::new();
590 while let Some(c) = self.next() {
591 match c {
592 '>' => {
593 if !name.is_empty() {
594 return Ok(TagNode { name, attributes });
595 } else {
596 return Err(anyhow::anyhow!("Empty tag name"));
597 }
598 }
599 ' ' | '\t' => {
600 if is_name {
601 is_name = false;
602 is_key = true;
603 } else if is_key {
604 if !key.is_empty() {
605 attributes.push((key.clone(), TagAttr::True));
606 key.clear();
607 }
608 } else if is_value {
609 if is_in_quote {
610 value.push(c);
611 } else {
612 if !value.is_empty() {
613 attributes.push((key.clone(), TagAttr::Str(unescape_xml(&value))));
614 key.clear();
615 value.clear();
616 }
617 is_key = true;
618 is_value = false;
619 }
620 }
621 }
622 '"' => {
623 if is_in_quote {
624 is_in_quote = false;
625 if !value.is_empty() {
626 attributes.push((key.clone(), TagAttr::Str(unescape_xml(&value))));
627 key.clear();
628 value.clear();
629 }
630 is_key = true;
631 } else {
632 is_in_quote = true;
633 }
634 }
635 '=' => {
636 if is_key {
637 is_key = false;
638 is_value = true;
639 }
640 }
641 _ => {
642 if is_name {
643 name.push(c);
644 } else if is_key {
645 key.push(c);
646 } else if is_value {
647 value.push(c);
648 } else {
649 return Err(anyhow::anyhow!("Unexpected character in tag: {}", c));
650 }
651 }
652 }
653 }
654 Err(anyhow::anyhow!("Unexpected end of input while parsing tag"))
655 }
656
657 pub fn parse(mut self) -> Result<Vec<ParsedLine>> {
658 let mut lines = Vec::new();
659 let mut current_line = Vec::new();
660 let mut text = String::new();
661 while let Some(c) = self.next() {
662 match c {
663 '<' => {
664 if !text.is_empty() {
665 current_line.push(ParsedLineNode::Text(TextNode(unescape_xml(&text))));
666 text.clear();
667 }
668 let tag = self.parse_tag()?;
669 let is_r = tag.name == "r";
670 current_line.push(ParsedLineNode::Tag(tag));
671 if is_r {
672 lines.push(ParsedLine(current_line));
673 current_line = Vec::new();
674 }
675 }
676 _ => text.push(c),
677 }
678 }
679 if !text.is_empty() {
680 current_line.push(ParsedLineNode::Text(TextNode(unescape_xml(&text))));
681 }
682 current_line.push(ParsedLineNode::Tag(TagNode {
683 name: "np".to_string(),
684 attributes: Vec::new(),
685 }));
686 lines.push(ParsedLine(current_line));
687 Ok(lines)
688 }
689
690 fn next(&mut self) -> Option<char> {
691 if self.pos < self.str.len() {
692 let c = self.str[self.pos..].chars().next()?;
693 self.pos += c.len_utf8();
694 Some(c)
695 } else {
696 None
697 }
698 }
699}
700
701#[derive(Debug)]
702pub struct KsScript {
704 bom: BomType,
705 tree: ParsedScript,
706 name_commands: Arc<HashSet<String>>,
707 message_commands: Arc<HashSet<String>>,
708 remove_empty_lines: bool,
709}
710
711impl KsScript {
712 pub fn new(reader: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
718 let (text, bom) = decode_with_bom_detect(encoding, &reader, true)?;
719 let parser = Parser::new(&text);
720 let tree = parser.parse(!config.kirikiri_remove_empty_lines)?;
721 Ok(Self {
722 bom,
723 tree,
724 name_commands: config.kirikiri_name_commands.clone(),
725 message_commands: config.kirikiri_message_commands.clone(),
726 remove_empty_lines: config.kirikiri_remove_empty_lines,
727 })
728 }
729}
730
731impl Script for KsScript {
732 fn default_output_script_type(&self) -> OutputScriptType {
733 OutputScriptType::Json
734 }
735
736 fn default_format_type(&self) -> FormatOptions {
737 FormatOptions::None
738 }
739
740 fn extract_messages(&self) -> Result<Vec<Message>> {
741 let mut messages = Vec::new();
742 let mut name = None;
743 let mut message = String::new();
744 for obj in self.tree.iter() {
745 match obj {
746 ParsedScriptNode::Label(_) => {
747 if !message.is_empty() {
748 messages.push(Message {
749 name: name.clone(),
750 message: message.trim_end_matches("<np>").to_owned(),
751 });
752 message.clear();
753 name = None;
754 }
755 }
756 ParsedScriptNode::Line(line) => {
757 if !message.ends_with("<np>") {
758 message.push_str(&line.to_xml())
759 }
760 }
761 ParsedScriptNode::Command(cmd) => {
762 if self.name_commands.contains(&cmd.name) {
763 for attr in &cmd.attributes {
764 if let TagAttr::Str(value) = &attr.1 {
765 if !value.is_empty() && !value.is_ascii() {
766 name = Some(value.clone());
767 break; }
769 }
770 }
771 } else if self.message_commands.contains(&cmd.name) {
772 for attr in &cmd.attributes {
773 if let TagAttr::Str(value) = &attr.1 {
774 if !value.is_empty() && !value.is_ascii() {
775 messages.push(Message {
776 name: None,
777 message: value.clone(),
778 });
779 break; }
781 }
782 }
783 }
784 }
785 _ => {}
786 }
787 }
788 if !message.is_empty() {
789 messages.push(Message {
790 name,
791 message: message.trim_end_matches("<np>").to_owned(),
792 });
793 }
794 Ok(messages)
795 }
796
797 fn import_messages<'a>(
798 &'a self,
799 messages: Vec<Message>,
800 mut file: Box<dyn WriteSeek + 'a>,
801 _filename: &str,
802 encoding: Encoding,
803 replacement: Option<&'a ReplacementTable>,
804 ) -> Result<()> {
805 let mut mes = messages.iter();
806 let mut cur_mes = None;
807 let mut tree = self.tree.clone();
808 let mut message_lines = Vec::new();
809 let mut i = 0;
810 let mut is_end = false;
811 let mut name_command_block_line: Option<(usize, String)> = None;
812 while i < tree.len() {
813 match tree[i].clone() {
814 ParsedScriptNode::Label(_) => {
815 if !message_lines.is_empty() {
816 let m: &Message = cur_mes
817 .take()
818 .ok_or(anyhow::anyhow!("Not enough messages"))?;
819 if let Some((line, key)) = name_command_block_line.take() {
820 let name = m
821 .name
822 .as_ref()
823 .ok_or(anyhow::anyhow!("Name not found in message"))?;
824 let mut name = name.clone();
825 if let Some(replacement) = replacement {
826 for (key, value) in replacement.map.iter() {
827 name = name.replace(key, value);
828 }
829 }
830 tree[line].set_attr(&key, name);
831 }
832 let mut text = m.message.to_owned();
833 if let Some(replacement) = replacement {
834 for (key, value) in replacement.map.iter() {
835 text = text.replace(key, value);
836 }
837 }
838 let mess = XMLTextParser::new(&text).parse()?;
839 let diff = mess.len() as isize - message_lines.len() as isize;
840 let common_lines = message_lines.len().min(mess.len());
841 let mut last_index = message_lines.last().cloned().unwrap_or(0);
842 for j in 0..common_lines {
843 tree[message_lines[j]] = ParsedScriptNode::Line(mess[j].clone());
844 }
845 for j in common_lines..message_lines.len() {
846 tree.remove(message_lines[j] - (j - common_lines));
847 }
848 for i in common_lines..mess.len() {
849 let new_line = ParsedScriptNode::Line(mess[i].clone());
850 if last_index < tree.len() {
851 tree.insert(last_index + 1, new_line);
852 last_index += 1;
853 } else {
854 tree.push(new_line);
855 }
856 }
857 i = (i as isize + diff) as usize;
858 }
859 message_lines.clear();
860 is_end = false;
861 if cur_mes.is_none() {
862 cur_mes = mes.next();
863 }
864 }
865 ParsedScriptNode::Line(line) => {
866 if !is_end {
867 message_lines.push(i);
868 is_end = line.last().map(|e| e.is_np()).unwrap_or(false);
869 }
870 }
871 ParsedScriptNode::Command(cmd) => {
872 if self.name_commands.contains(&cmd.name) {
873 for attr in &cmd.attributes {
874 if let TagAttr::Str(value) = &attr.1 {
875 if !value.is_empty() && !value.is_ascii() {
876 name_command_block_line = Some((i, attr.0.clone()));
877 break; }
879 }
880 }
881 } else if self.message_commands.contains(&cmd.name) {
882 for attr in &cmd.attributes {
883 if let TagAttr::Str(value) = &attr.1 {
884 if !value.is_empty() && !value.is_ascii() {
885 let m = cur_mes
886 .take()
887 .ok_or(anyhow::anyhow!("Not enough messages"))?;
888 let mut text = m.message.clone();
889 if let Some(replacement) = replacement {
890 for (key, value) in replacement.map.iter() {
891 text = text.replace(key, value);
892 }
893 }
894 tree[i].set_attr(&attr.0, text);
895 cur_mes = mes.next();
896 break; }
898 }
899 }
900 }
901 }
902 _ => {}
903 }
904 i += 1;
905 }
906 if !message_lines.is_empty() {
907 let m: &Message = cur_mes
908 .take()
909 .ok_or(anyhow::anyhow!("Not enough messages"))?;
910 if let Some((line, key)) = name_command_block_line.take() {
911 let name = m
912 .name
913 .as_ref()
914 .ok_or(anyhow::anyhow!("Name not found in message"))?;
915 let mut name = name.clone();
916 if let Some(replacement) = replacement {
917 for (key, value) in replacement.map.iter() {
918 name = name.replace(key, value);
919 }
920 }
921 tree[line].set_attr(&key, name);
922 }
923 let mut text = m.message.to_owned();
924 if let Some(replacement) = replacement {
925 for (key, value) in replacement.map.iter() {
926 text = text.replace(key, value);
927 }
928 }
929 let mess = XMLTextParser::new(&text).parse()?;
930 let common_lines = message_lines.len().min(mess.len());
931 let mut last_index = message_lines.last().cloned().unwrap_or(0);
932 for j in 0..common_lines {
933 tree[message_lines[j]] = ParsedScriptNode::Line(mess[j].clone());
934 }
935 for j in common_lines..message_lines.len() {
936 tree.remove(message_lines[j] - (j - common_lines));
937 }
938 for i in common_lines..mess.len() {
939 let new_line = ParsedScriptNode::Line(mess[i].clone());
940 if last_index < tree.len() {
941 tree.insert(last_index + 1, new_line);
942 last_index += 1;
943 } else {
944 tree.push(new_line);
945 }
946 }
947 }
948 if cur_mes.is_some() || mes.next().is_some() {
949 return Err(anyhow::anyhow!("Some messages were not processed."));
950 }
951 if self.remove_empty_lines {
952 tree.retain(|node| !node.is_empty());
953 }
954 let s = tree.serialize() + "\n";
955 let data = encode_string_with_bom(encoding, &s, false, self.bom)?;
956 file.write_all(&data)?;
957 Ok(())
958 }
959}